/**
 * GraphicChessboard represents the graphic instance of a chess game.
 * 
 * @author Brian Boadi, Antoine Domenger
 * @version 1.0
 */

package lp.chess.ui;

import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;

import javax.imageio.ImageIO;
import javax.swing.SwingUtilities;

import lp.chess.BlackPlayer;
import lp.chess.Chess;
import lp.chess.Chessboard;
import lp.chess.Piece;
import lp.chess.WhitePlayer;

public class GraphicChessboard {
	//- ATTRIBUTE(S) --------------------------------------------------------------------------
	
	private JCanvas m_gc;
	private Dimension m_dim;
	private List<IDrawable> m_cases;
	private List<IDrawable> m_pieces;
	private IDrawable m_selected;
	private Piece m_selectedPiece;
	
	private Stack<Chessboard> m_previous;
	private Stack<Chessboard> m_next;
	
	private Stack<WhitePlayer> m_previousWP;
	private Stack<WhitePlayer> m_nextWP;
	private Stack<BlackPlayer> m_nextBP;
	private Stack<BlackPlayer> m_previousBP;
	
	//-----------------------------------------------------------------------------------------
	
	//- CONSTRUCTOR(S) ------------------------------------------------------------------------
	
	public GraphicChessboard() {
		m_gc = new JCanvas();
		m_gc.setBackground(Color.WHITE);
		m_gc.setPreferredSize(new Dimension(512,512));
		
		m_dim = new Dimension(64,64);
		
		m_cases = new LinkedList<IDrawable>();
		m_pieces = new LinkedList<IDrawable>();
		m_selected = null;
		
		m_selectedPiece = null;
		
		m_previous = new Stack<Chessboard>();
		m_next = new Stack<Chessboard>();
		
		m_previousWP = new Stack<WhitePlayer>();
		m_nextWP = new Stack<WhitePlayer>();
		m_nextBP = new Stack<BlackPlayer>();
		m_previousBP = new Stack<BlackPlayer>();
	}
	
	//-----------------------------------------------------------------------------------------
	
	//- OTHER METHOD(S) -----------------------------------------------------------------------
	
	public void restart() {
		first();
		m_next.clear();
		m_nextWP.clear();
		m_nextBP.clear();
	}
	
	public void first() {
		Chessboard current; 
		WhitePlayer currentWP;
		BlackPlayer currentBP;
		
		if(!m_previous.isEmpty() && !m_previousWP.isEmpty() && !m_previousBP.isEmpty()){
			current = new Chessboard(Chess.getChessboard());
			currentWP = new WhitePlayer(Chess.getWhitePlayer());
			currentBP = new BlackPlayer(Chess.getBlackPlayer());
			
			while(!m_previous.isEmpty() && !m_previousWP.isEmpty() && !m_previousBP.isEmpty()) {
				m_next.push(current);
				m_nextWP.push(currentWP);
				m_nextBP.push(currentBP);
				
				current = m_previous.pop();
				currentWP = m_previousWP.pop();
				currentBP = m_previousBP.pop();
				
				Chess.setTurn(Chess.getTurn() - 1);
				Chess.setWhiteIsPlaying(!Chess.isWhitePlaying());
			}
			
			Chess.setChessboard(current);
			Chess.setWhitePlayer(currentWP);
			Chess.setBlackPlayer(currentBP);
			
			m_selectedPiece = null;
			m_selected = null;
			
			refresh(current);
		}
	}
	
	public void back() {
		Chessboard current; 
		WhitePlayer currentWP;
		BlackPlayer currentBP;
		
		if(!m_previous.isEmpty() && !m_previousWP.isEmpty() && !m_previousBP.isEmpty()) {
			current = new Chessboard(Chess.getChessboard());
			currentWP = new WhitePlayer(Chess.getWhitePlayer());
			currentBP = new BlackPlayer(Chess.getBlackPlayer());
			
			m_next.push(current);
			m_nextWP.push(currentWP);
			m_nextBP.push(currentBP);
			
			current = m_previous.pop();
			currentWP = m_previousWP.pop();
			currentBP = m_previousBP.pop();
			
			Chess.setChessboard(current);
			Chess.setWhitePlayer(currentWP);
			Chess.setBlackPlayer(currentBP);
			
			Chess.setTurn(Chess.getTurn() - 1);
			Chess.setWhiteIsPlaying(!Chess.isWhitePlaying());
			
			m_selectedPiece = null;
			m_selected = null;
			
			refresh(current);
		}
	}
	
	public void next() {
		Chessboard current;
		WhitePlayer currentWP;
		BlackPlayer currentBP;
		
		if(!m_next.isEmpty() && !m_nextWP.isEmpty() && !m_nextBP.isEmpty()) {
			current = new Chessboard(Chess.getChessboard());
			currentWP = new WhitePlayer(Chess.getWhitePlayer());
			currentBP = new BlackPlayer(Chess.getBlackPlayer());
			
			m_previous.push(current);
			m_previousWP.push(currentWP);
			m_previousBP.push(currentBP);
			
			current = m_next.pop();
			currentWP = m_nextWP.pop();
			currentBP = m_nextBP.pop();
			
			Chess.setChessboard(current);
			Chess.setWhitePlayer(currentWP);
			Chess.setBlackPlayer(currentBP);
			
			Chess.setTurn(Chess.getTurn() + 1);
			Chess.setWhiteIsPlaying(!Chess.isWhitePlaying());
			
			m_selectedPiece = null;
			m_selected = null;
			
			refresh(current);
		}
	}
	
	public void last() {
		Chessboard current; 
		WhitePlayer currentWP;
		BlackPlayer currentBP;
		
		if(!m_next.isEmpty() && !m_nextWP.isEmpty() && !m_nextBP.isEmpty()) {
			current = new Chessboard(Chess.getChessboard());
			currentWP = new WhitePlayer(Chess.getWhitePlayer());
			currentBP = new BlackPlayer(Chess.getBlackPlayer());
			
			while(!m_next.isEmpty()) {
				m_previous.push(current);
				m_previousWP.push(currentWP);
				m_previousBP.push(currentBP);
				
				current = m_next.pop();
				currentWP = m_nextWP.pop();
				currentBP = m_nextBP.pop();
				
				Chess.setTurn(Chess.getTurn() + 1);
				Chess.setWhiteIsPlaying(!Chess.isWhitePlaying());
			}
			
			Chess.setChessboard(current);
			Chess.setWhitePlayer(currentWP);
			Chess.setBlackPlayer(currentBP);
			
			m_selectedPiece = null;
			m_selected = null;
			
			refresh(current);
		}
	}
	
	public void addListeners() {
		m_gc.addMouseListener(new MouseAdapter() {
			public void  mouseClicked(MouseEvent e) {
				// We fetch the coordinates and the current chessboard:
				Point ptClicked = e.getPoint();
				Chessboard cb = Chess.getChessboard();
				
				if(SwingUtilities.isLeftMouseButton(e)) {	// Left click.
					// With convert the coordinates in terms of cases:
					int x = (int)(ptClicked.getX() / 64) + 1;
					int y = (int)(ptClicked.getY() / 64) + 1;
					
					// We get the piece at the specified coordinates:
					Piece selectedPiece = cb.getPiece(x, y);
					
					if(selectedPiece != null) {	// There is actually a piece there.
						if(selectedPiece.isWhite() == Chess.isWhitePlaying()) {	// The color of the chosen piece is the current player's color.
							// We select this piece:
							m_selectedPiece = selectedPiece;
							
							// We show that the piece has been selected:
							m_selected = new RectangleDrawable(Color.GREEN, new Point((x-1)*64,(y-1)*64), m_dim);
							
							refresh(cb);
						}
					}
				}
				else {	// Right click.
					if(m_selectedPiece != null) {			
						// With convert the coordinates in terms of cases:
						int x = (int)(ptClicked.getX() / 64) + 1;
						int y = (int)(ptClicked.getY() / 64) + 1;
						
						Chessboard bufferedChessboard = new Chessboard(cb);
						WhitePlayer bufferedWP = new WhitePlayer(Chess.getWhitePlayer());
						BlackPlayer bufferedBP = new BlackPlayer(Chess.getBlackPlayer());
						
						if(m_selectedPiece.validMove(cb, x, y, true)) {	// The movement is valid.
							if(cb.move(m_selectedPiece, x, y)) {	// The piece has been moved.
								boolean isWhitePlaying = Chess.isWhitePlaying();
								
								// We prepare the next action:
								Chess.setTurn(Chess.getTurn() + 1);
								Chess.setWhiteIsPlaying(!isWhitePlaying);
								
								m_selectedPiece = null;
								m_selected = null;
								
								if(!m_next.isEmpty()) {
									m_next.clear();
								}
								
								// We refresh the graphic representation of the chessboard:
								refresh(cb);
								
								// We push the old chessboard into the stack:
								m_previous.push(bufferedChessboard);
								m_previousWP.push(bufferedWP);
								m_previousBP.push(bufferedBP);
								
								// We check if any player is in check:
								if(Chess.getWhitePlayer().isInCheck(cb)) {
									Chess.setWhiteInCheck(true);
									Chess.getMainForm().popupDialog("White is in check.");
								}
								if(Chess.getBlackPlayer().isInCheck(cb)) {
									Chess.setBlackInCheck(true);
									Chess.getMainForm().popupDialog("Black is in check.");
								}
							}
						}
					}
				}
			}
		});
	}
	
	//-----------------------------------------------------------------------------------------
	
	//- GETTER(S) AND SETTER(S) ---------------------------------------------------------------
	
	public JCanvas getJCanvas() {
		return m_gc;
	}
	
	//-----------------------------------------------------------------------------------------
	
	//- DISPLAY METHOD(S) ----------------------------------------------------------------------
	
	public void refresh(Chessboard cb) {
		m_gc.clear();
		drawBackground(m_gc.getGraphics());
		if(m_selected != null) drawSelectedPiece(m_gc.getGraphics());
		drawPieces(cb, m_gc.getGraphics());
	}
	
	public void drawSelectedPiece(Graphics g) {
		m_gc.addDrawable(m_selected);
	}
	
	public void drawBackground(Graphics g) {
		int x,y;
		
		m_cases.clear();
		
		// White cases:
		g.setColor(Color.WHITE);
		
		for(x = 0 ; x < 512 ; x += 128)
		{
			for(y = 0 ; y < 512 ; y += 128)
			{
				m_cases.add(new RectangleDrawable(Color.WHITE,new Point(x,y),m_dim));
			}
		}
		
		for(x = 64 ; x < 512 ; x += 128)
		{
			for(y = 64 ; y < 512 ; y += 128)
			{
				m_cases.add(new RectangleDrawable(Color.WHITE,new Point(x,y),m_dim));
			}
		}
		
		// Black cases:
		g.setColor(Color.GRAY);
		
		for(x = 64 ; x < 512 ; x += 128)
		{
			for(y = 0 ; y < 512 ; y += 128)
			{
				m_cases.add(new RectangleDrawable(Color.GRAY,new Point(x,y),m_dim));
			}
		}
		
		for(x = 0 ; x < 512 ; x += 128)
		{
			for(y = 64 ; y < 512 ; y += 128)
			{
				m_cases.add(new RectangleDrawable(Color.GRAY,new Point(x,y),m_dim));
			}
		}
		
		m_gc.addDrawables(m_cases);
	}
	
	public void drawPieces(Chessboard cb, Graphics g) {
		int x, y;
		File f = null;
		Image img;
		
		m_pieces.clear();
		
		for(y = 1 ; y <= 8 ; ++y)
  		{
  			for(x = 1 ; x <= 8 ; ++x)
  			{
  				Piece p = cb.getPiece(x, y);
  				
  				if(p != null) {
  					char c = p.getType();
  					
  					switch(c) {
  					case 'K' :
  						f = new File("img/white_king.png");
  						break;
  						
  					case 'k' :
  						f = new File("img/black_king.png");
  						break;
  						
  					case 'Q' :
  						f = new File("img/white_queen.png");
  						break;
  						
  					case 'q' :
  						f = new File("img/black_queen.png");
  						break;
  						
  					case 'N' :
  						f = new File("img/white_knight.png");
  						break;
  						
  					case 'n' :
  						f = new File("img/black_knight.png");
  						break;
  						
  					case 'B' :
  						f = new File("img/white_bishop.png");
  						break;
  						
  					case 'b' :
  						f = new File("img/black_bishop.png");
  						break;
  						
  					case 'R' :
  						f = new File("img/white_rook.png");
  						break;
  						
  					case 'r' :
  						f = new File("img/black_rook.png");
  						break;
  						
  					case 'P' :
  						f = new File("img/white_pawn.png");
  						break;
  						
  					case 'p' :
  						f = new File("img/black_pawn.png");
  						break;
  						
  					}
  					
  					try {
						img = ImageIO.read(f);
						m_pieces.add(new ImageDrawable(img, new Point((x-1)*64,(y-1)*64)));
					}
					catch (IOException e1) {
						System.out.println("Error opening the image file...");
					}
  				}
  			}
  			
  			m_gc.addDrawables(m_pieces);
  		}
	}
	
	//-----------------------------------------------------------------------------------------
}
